/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.common;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.entity.MetadataType;
import com.inspur.edp.lcm.metadata.api.entity.OperationEnum;
import com.inspur.edp.lcm.metadata.api.exception.ErrorCodes;
import com.inspur.edp.lcm.metadata.api.exception.LcmFileDevException;
import com.inspur.edp.lcm.metadata.api.exception.LcmMetadataDevException;
import com.inspur.edp.lcm.metadata.common.configuration.GspProjectExtendHelper;
import com.inspur.edp.lcm.metadata.common.configuration.MetadataTypeHelper;
import com.inspur.edp.lcm.metadata.devcommon.ManagerUtils;
import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.runtime.CafEnvironment;
import com.inspur.edp.lcm.metadata.common.ServiceUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

@Slf4j
public class Utils extends ServiceUtils {
    public static final String DEFAULT_PACKAGE_PREFIX = "com";
    public static final String DOT = ".";
    public static final String HYPHON = "-";
    public static final String BO = "bo";
    public static final String GENERATE = "generate";
    public static final String COMPILE = "compile";
    public static final String JITTODLL = "JitToDll";
    public static final String LAST_CHANGE_ON = ".lastChangeOn";
    public static final String LAST_CHANGE_BY = ".lastChangeBy";
    public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 发布流程中使用，为了使进度条跳跃更加平缓
     */
    public static final long PUBLISH_WAIT_TIME = 1;
    private static final Lock lock = new ReentrantLock();
    private static final String DEFAULT_USER = "defaultuser";
    //元数据工程后缀
    private static String metadataProjSuffix;
    // pom后缀
    private static String pomSuffix;
    // 元数据包version前缀
    private static String versionPrefix;
    // 元数据包临时version后缀
    private static String versionSuffix;
    //csproj工程后缀
    private static String csprojSuffix;
    //GSP工程描述文件名称
    private static String gspProjectName;
    //元数据包后缀
    private static String metadataPackageExtension;
    //元数据包清单文件
    private static String manifestFileName;
    //原始数据修改时间文件
    private static String sourceDataFileName;
    // 强制更新依赖jar包标识文件
    private static String mavenUpdateFlagFileName;
    private static String metadataPath;
    /**
     * 获取maven工程名称。
     */
    private static String mavenProName;
    /**
     * edpParentVersion
     */
    private static String edpParentVersion;
    private static List<String> metadataPostfixTypes;
    private static final Map<String, String> userPathMap = new HashMap<>();

    private static final String USER_LAST_CHANGED_BY = "user:lastChangedOn";

    public Utils() {
    }

    public static String getMetadataProjSuffix() {
        return ".mdproj";
    }

    public static String getPomSuffix() {
        return ".pom";
    }

    public static String getVersionPrefix() {
        return "m";
    }

    public static String getVersionSuffix() {
        return "-SNAPSHOT";
    }

    public static String getCsprojSuffix() {
        return ".csproj";
    }

    public static String getGspProjectName() {
        return "gspproject.json";
    }

    public static String getSourceDataFileName() {
        return "sourcedata.json";
    }

    public static String getMavenUpdateFlagFileName() {
        return "mavenupdateflag.txt";
    }

    public static String getApiSourceDir() {
        return "java/code/api/src";
    }

    public static String getApiTargetDir() {
        return "java/code/api/target";
    }

    public static String getCompModuleDir() {
        return "java/code/comp";
    }

    public static String getRuntimeSourceDir() {
        return "java/code/runtime/src";
    }

    public static String getSrcJavaPath() {
        return "src/main/java";
    }

    public static String getResourcesPath() {
        return "src/main/resources";
    }

    public static String getPomFile() {
        return "pom.xml";
    }

    public static String getJavaProjectPath() {
        return "java";
    }

    public static String getZipSuffix() {
        return ".zip";
    }

    public static String getJarSuffix() {
        return ".jar";
    }

    public static String getLanguageMetadataFileExtention() {
        return ".lres";
    }

    public static String getPublishPath() {
        return "publish";
    }

    public static String getServerPath() {
        return new File(CafEnvironment.getServerRTPath()).getName();
    }

    public static String getCodePath() {
        return "code";
    }

    public static String getSrcPath() {
        return "src";
    }

    public static String getTargetPath() {
        return "target";
    }

    public static String getLibsPath() {
        return "libs";
    }

    // 获取安装盘的基路径

    public static String getMavenProName() {
        if (mavenProName == null || mavenProName.isEmpty()) {
            return GspProjectExtendHelper.getInstance().getMavenProjectName();
        }
        return null;
    }

    public static String getEdpParentVersion() {
        if (mavenProName == null || mavenProName.isEmpty()) {
            return GspProjectExtendHelper.getInstance().getEdpParentVersion();
        }
        return null;
    }

    public static String handlePackageName(String packageName) {
        String packageFullName;
        if (!packageName.contains(getMetadataPackageExtension())) {
            packageFullName = packageName + getMetadataPackageExtension();
        } else {
            packageFullName = packageName;
        }

        return packageFullName;
    }

    public static List<String> getMetadataPostfixTypes() {
        List<MetadataType> metadataTypes = MetadataTypeHelper.getInstance().getMetadataTypes();
        return getMetadataPostfixTypes(metadataTypes);
    }

    public static List<String> getMetadataPostfixTypes(OperationEnum operationEnum) {
        List<MetadataType> metadataTypes = MetadataTypeHelper.getInstance().getMetadataTypes(operationEnum);
        return getMetadataPostfixTypes(metadataTypes);
    }

    private static List<String> getMetadataPostfixTypes(List<MetadataType> metadataTypes) {
        List<String> metadataPostfixTypes = new ArrayList<>();
        metadataTypes.forEach(metadataType -> metadataPostfixTypes.add(metadataType.getPostfix()));
        return metadataPostfixTypes;
    }

    /**
     * 将源文件/文件夹生成指定格式的压缩文件,格式zip
     *
     * @param resourcesPath 源文件/文件夹
     * @param targetPath    目的压缩文件保存路径
     * @param targetName    目的压缩文件名
     */
    public static void compress(String resourcesPath, String targetPath, String targetName) {
        //源文件
        File resourcesFile = new File(resourcesPath);
        //目的
        File targetFile = new File(targetPath);
        //如果目的路径不存在，则新建
        if (!targetFile.exists()) {
            targetFile.mkdirs();
        }
        //目的压缩文件名
        targetName = targetName + ".mdpkg";
        try {
            FileOutputStream outputStream = new FileOutputStream(FileServiceImp.combinePath(targetPath, targetName));
            ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(outputStream));

            createCompressedFile(out, resourcesFile, "");

            out.close();
        } catch (Exception e) {
            throw new LcmFileDevException(e, ErrorCodes.ECP_FILE_DEV_0005, resourcesPath);
        }
    }

    /**
     * 生成压缩文件。如果是文件夹，则使用递归，进行文件遍历、压缩 如果是文件，直接压缩
     *
     * @param out  输出流
     * @param file 目标文件
     * @param dir  文件路径
     */
    public static void createCompressedFile(ZipOutputStream out, File file, String dir) {
        //如果当前的是文件夹，则进行进一步处理
        if (file.isDirectory()) {
            //得到文件列表信息
            File[] files = file.listFiles();
            try {
                //将文件夹添加到下一级打包目录
                out.putNextEntry(new ZipEntry(dir + "/"));
            } catch (Exception e) {
                throw new LcmFileDevException(e, ErrorCodes.ECP_FILE_DEV_0005, file.getPath());
            }

            dir = dir.length() == 0 ? "" : dir + "/";

            //循环将文件夹中的文件打包
            for (int i = 0; i < files.length; i++) {
                createCompressedFile(out, files[i], dir + files[i].getName());         //递归处理
            }
        } else {   //当前的是文件，打包处理
                   //文件输入流
            try (FileInputStream fis = new FileInputStream(file)) {

                out.putNextEntry(new ZipEntry(dir));
                //进行写操作
                int j = 0;
                byte[] buffer = new byte[1024];
                while ((j = fis.read(buffer)) > 0) {
                    out.write(buffer, 0, j);
                }
            } catch (Exception e) {
                throw new LcmFileDevException(e, ErrorCodes.ECP_FILE_DEV_0005, file.getPath());
            }
        }
    }

    public static void writeValue(String mdPath, MetadataProject mdProj) {
        try (FileOutputStream fileOutputStream = new FileOutputStream(mdPath)) {
            ObjectMapper objectMapper = getMapper();
            objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
            if (!StringUtils.isEmpty(mdProj.getProjectPath())) {
                mdProj.setProjectPath(ManagerUtils.getRelativePath(mdProj.getProjectPath()));
            }
            objectMapper.writeValue(fileOutputStream, mdProj);
        } catch (IOException e) {
            throw new LcmMetadataDevException(e, ErrorCodes.ECP_METADATA_DEV_0002, mdPath);
        }
    }

    public static boolean nameValidating(String name) {
        if (StringUtils.isEmpty(name)) {
            return false;
        } else if (name.trim().length() > 255) {
            return false;
        } else {
            return StringUtils.indexOfAny(name.trim(), getInValidCharacter()) == -1;
        }
    }

    public static String[] getInValidCharacter() {
        return new String[]{"\\", "/", ":", "*", "?", "\"", "<", ">", "|", "&", "@", "\"", "‘", "’", "“", "”", "%", "；", ";"};
    }

    @Deprecated
    public static boolean isNullOrEmpty(String str) {
        return str == null || str.isEmpty();
    }

    @Deprecated
    public static boolean isNullOrEmpty(List list) {
        return list == null || list.size() == 0;
    }


    //从上下文中获取用户id，用于隔离用户操作中的path信息
    //如果无上下文，获取userid可能会报错，此种情况对应在命令行工具运行，应该能够正常运行，使用defaultuser
    //如果上下文中获取出来userid为空，此种情况对应在命令行工具中运行？应该能够正常运行
    public static String getMetadataPath() {
        String userId = null;
        try {
            userId = CAFContext.current.getUserId();
        } catch (Exception e) {
            return userPathMap.get(DEFAULT_USER);
        }
        if (org.springframework.util.StringUtils.isEmpty(userId)) {
            return userPathMap.get(DEFAULT_USER);
        } else {
            return userPathMap.get(userId);
        }
    }

    public static void setMetadataPath(String metadataPath) {
        String userId = null;
        try {
            userId = CAFContext.current.getUserId();
        } catch (Exception e) {
            userPathMap.put(DEFAULT_USER, metadataPath);
        }
        if (org.springframework.util.StringUtils.isEmpty(userId)) {
            userPathMap.put(DEFAULT_USER, metadataPath);
        } else {
            userPathMap.put(userId, metadataPath);
        }
    }

    /**
     * 获取当前安装盘下提供元数据包所在地址
     *
     * @return 安装盘下元数据包所在地址
     * @author sunhongfei
     */
    public static String getServerMetadataPackDir() {
        return FileServiceImp.combinePath(getBasePath(), "metadata", "platform");
    }

    public static void setFileAttrLastChangedBy(String path) {
        // 更新最后修改人
        try {
            Files.setAttribute(Paths.get(path), USER_LAST_CHANGED_BY, CAFContext.current.getUserId().getBytes(StandardCharsets.UTF_8));
        } catch (Throwable e) {
            log.error("Failed to Update lastChangedBy: {}", path, e);
        }
    }

    public static String getFileAttrLastChangedBy(String pathStr) {
        // 文件不存在，直接返回null
        Path path = Paths.get(pathStr);
        if (Files.notExists(path)) {
            return null;
        }

        // 更新最后修改人
        try {
            Object lastChangedBy = Files.getAttribute(path, USER_LAST_CHANGED_BY);
            if (lastChangedBy != null) {
                return new String((byte[])lastChangedBy);
            }
        } catch (NoSuchFileException e) {
            // 文件没有设置属性的时候，会抛这个异常，java.nio.file.NoSuchFileException: D:\work\projects\scm\sd\salesorder\bo-salesorder-back\metadata\be\yi.be.res:lastChangedOn
            log.debug("File {} has no attribute: {}", path, USER_LAST_CHANGED_BY, e);
        } catch (Throwable e) {
            log.error("Failed to get lastChangedBy: {}", path, e);
        }
        return null;
    }
}
